package com.smart.tool.transaction;

import java.util.List;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.core.NamedThreadLocal;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @Author gao.gwq
 * @Version TransactionFuture.java, v 0.1 2024年03月01日 14:46 gao.gwq
 * @Description: TODO
 */
public class TransactionFuture<T> {
    private final static ThreadLocal<List<TransactionFuture>> TRANSACTION_LOCAL = new NamedThreadLocal<>("transaction local");


    private TransactionFuture() {
    }

    /**
     * 是否取消执行的状态，如果这个值为true，则事务片段不会被执行，如果为false 则会被执行
     * true 取消执行
     * false 执行
     */
    private Boolean cancelFlag = false;

    private T result;

    /**
     * 是否执行成功
     * false 未执行成功
     * true  执行成功
     */
    private boolean isSucess = false;


    private Throwable throwable;


    private Action<T> action;


    public boolean isSucess() {
        return isSucess;
    }

    public Throwable getThrowable() {
        return throwable;
    }

    public T getResult() {

        if (throwable != null) {
            throw new RuntimeException(throwable);
        }
        if (result == null) {
            return null;
        }

        return (T) result;
    }


    /**
     * 取消事务事件的执行
     *
     * @param future
     * @param <T>
     */
    public static <T> void cancelTransaction(TransactionFuture<T> future) {
        future.cancelFlag = true;
    }

    /**
     * 创建一个新的事物片段
     * @param action
     * @return
     * @param <T>
     */
    public static <T> TransactionFuture<T> ofTransactionFuture(Action<T> action) {
        if(action == null) {
            throw new IllegalArgumentException("action is null");
        }
        TransactionFuture<T> transactionFuture = new TransactionFuture<>();
        transactionFuture.action = action;
        return transactionFuture;
    }

    /**
     * 执行注册生成的事务片段
     * @param transactionTemplate
     * @param futures
     */
    public static void execute(TransactionTemplate transactionTemplate,TransactionFuture... futures){
        if(transactionTemplate == null) {
            throw new IllegalArgumentException("transactionTemplate is null");
        }
        if(futures == null) {
            throw new IllegalArgumentException("futures is null");
        }
        if(futures.length == 0) {
            throw new IllegalArgumentException("futures is empty");
        }
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                TransactionContext transactionContext = new TransactionContext(status,
                        TransactionSynchronizationManager.getCurrentTransactionName());

                //执行哪段代码抛出的异常，则直接执行对应的异常处理逻辑。
                TransactionFuture transactionFuture = null;
                try {
                    for (TransactionFuture future : futures) {
                        //如果被取消则不会执行该事物片段
                        if (future.cancelFlag) {
                            continue;
                        }
                        TransactionUtils.registerTrancationAdapter(transactionContext, future.action);
                        transactionFuture = future;
                        Object object = future.action.doAction(transactionContext);
                        future.result = object;
                        future.isSucess = true;
                    }

                } catch (RuntimeException throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } catch (Error throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } catch (Throwable throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } finally {
                    TRANSACTION_LOCAL.remove();
                }
            }
        });
    }

    public static <T> TransactionFuture<T> registerTransaction(Action<T> action) {
        TransactionFuture<T> transactionFuture = new TransactionFuture<>();
        transactionFuture.action = action;
        List<TransactionFuture> transactionFutures = TRANSACTION_LOCAL.get();
        if (transactionFutures == null) {
            transactionFutures = new java.util.ArrayList<>();
            TRANSACTION_LOCAL.set(transactionFutures);
        }
        transactionFutures.add(transactionFuture);

        return transactionFuture;
    }

    /**
     * 立即执行当前事务，如果调用本方法，不会执行之前注册的事务事件，执行后的事件会总注册列表中被清除
     *
     * @param transactionTemplate
     */
    public void executeNow(TransactionTemplate transactionTemplate) {
        List<TransactionFuture> futureList = TRANSACTION_LOCAL.get();
        if (CollectionUtils.isEmpty(futureList)) {
            return;
        }
        final TransactionFuture future = this;
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                TransactionContext transactionContext = new TransactionContext(status,
                        TransactionSynchronizationManager.getCurrentTransactionName());


                TransactionUtils.registerTrancationAdapter(transactionContext, future.action);
                //执行哪段代码抛出的异常，则直接执行对应的异常处理逻辑。
                TransactionFuture transactionFuture = null;
                try {
                    transactionFuture = future;
                    Object object = future.action.doAction(transactionContext);
                    future.result = object;
                    future.isSucess = true;

                } catch (RuntimeException throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;
                } catch (Error throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } catch (Throwable throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;
                } finally {
                    TRANSACTION_LOCAL.get().remove(future);
                }
            }
        });
    }

    public static void execute(TransactionTemplate transactionTemplate) {
        List<TransactionFuture> futureList = TRANSACTION_LOCAL.get();
        if (CollectionUtils.isEmpty(futureList)) {
            return;
        }
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                TransactionContext transactionContext = new TransactionContext(status,
                        TransactionSynchronizationManager.getCurrentTransactionName());

                //执行哪段代码抛出的异常，则直接执行对应的异常处理逻辑。
                TransactionFuture transactionFuture = null;
                try {
                    for (TransactionFuture future : futureList) {
                        //如果被取消则不会执行该事物片段
                        if (future.cancelFlag) {
                            continue;
                        }
                        TransactionUtils.registerTrancationAdapter(transactionContext, future.action);
                        transactionFuture = future;
                        Object object = future.action.doAction(transactionContext);
                        future.result = object;
                        future.isSucess = true;
                    }

                } catch (RuntimeException throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } catch (Error throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } catch (Throwable throwable) {
                    transactionFuture.throwable = throwable;
                    Object errorResult = processRollback(status, transactionContext, throwable, transactionFuture.action);
                    transactionFuture.result = errorResult;

                } finally {
                    TRANSACTION_LOCAL.remove();
                }
            }
        });
    }

    /**
     * 异常处理
     *
     * @param status
     * @param transactionContext
     * @param throwable
     * @param action
     * @return
     */
    private static <T> T processRollback(TransactionStatus status, TransactionContext<T> transactionContext, Throwable throwable,

                                         Action action) {

        transactionContext.setThrowable(throwable);
        transactionContext.setSucess(false);
        status.setRollbackOnly();
        Object result = action.processError(transactionContext, throwable);
        return (T) result;
    }

}
